package fem2;

import inf.jlinalg.SolveFailedException;
import inf.text.ArrayFormat;

import java.io.IOException;

import org.junit.Test;

import fem2.analysis.Analysis;
import fem2.analysis.LinearStaticAnalysis;
import fem2.analysis.NonlinearStaticAnalysis;
import fem2.enu.EchoLevelType;
import fem2.enu.State;
import fem2.material.StVenantKirchhoffMM;
import fem2.material.cohesive_law.CohesiveLaw;
import fem2.material.cohesive_law.Mode1AbsCohesiveLaw;
import fem2.observer.ResistantGraphicalObserver;
import fem2.observer.StressObserver;

public abstract class AbstractPatchTestCrackElement {

	public Mesh createMeshT3() {
		Mesh mesh = new Mesh();
		double l = 1;
		double h = 1;
		Node n1 = mesh.addNode(new Node(0, 0));
		Node n2 = mesh.addNode(new Node(l, 0));
		Node n3 = mesh.addNode(new Node(l, h));
		Node n4 = mesh.addNode(new Node(0, h));

		mesh.addFace(new Face(n1, n2, n3));
		mesh.addFace(new Face(n1, n3, n4));

		Constraint c1 = new Constraint(false, false);
		Constraint c2 = new Constraint(true, false);
		Constraint c3 = new Constraint(false, true);
		n1.setConstraint(c1);
		n2.setConstraint(c2);
		n4.setConstraint(c3);

		mesh.setNumberOfFields(2);

		return mesh;
	}

	public Mesh createMeshQ4() {
		Mesh mesh = new Mesh();
		double l = 1;
		double h = 1;
		Node n1 = mesh.addNode(new Node(0, 0));
		Node n2 = mesh.addNode(new Node(l, 0));
		Node n3 = mesh.addNode(new Node(l, h));
		Node n4 = mesh.addNode(new Node(0, h));

		mesh.addFace(new Face(n1, n2, n3, n4));

		Constraint c1 = new Constraint(false, false);
		Constraint c2 = new Constraint(true, false);
		Constraint c3 = new Constraint(false, true);
		n1.setConstraint(c1);
		n2.setConstraint(c2);
		n4.setConstraint(c3);

		mesh.setNumberOfFields(2);

		return mesh;
	}

	public MaterialModel createMaterial() {
		double E = 2;
		double nu = 0.3;
		double t = 1.0;
		State ss = State.PLANE_STRESS;
		return new StVenantKirchhoffMM(E, nu, t, 0, ss);
	}

	public CohesiveLaw createCohesiveLaw() {
		double H = -1;
		double ft = 0.91;
		double km = 0.0;
		double Gf = -(ft * ft) / (2 * H);
		return new Mode1AbsCohesiveLaw(Gf, ft, km);
	}

	public abstract Element createElement(MeshPart mp, MaterialModel mm, CohesiveLaw cl);

	public Model createModel(Mesh mesh) {

		Model m = new Model(mesh);

		MaterialModel mm = createMaterial();

		CohesiveLaw cl = createCohesiveLaw();

		for (int i = 0; i < mesh.countFaces(); i++) {
			Element e = createElement(mesh.getFace(i), mm, cl);
			m.addElement(e);
			// e.setEchoLevel(EchoLevelType.OUTPUT);
		}

		return m;
	}

	@Test
	public void testSolve1() throws SolveFailedException {

		Model m = createModel(createMeshT3());

		Mesh mesh = m.getMesh();

		double du = 1;
		Constraint c1 = new Constraint(false, false);
		c1.setValue(0, du);
		Constraint c2 = new Constraint(false, true);
		c2.setValue(0, du);
		mesh.getNode(1).setConstraint(c1);
		mesh.getNode(2).setConstraint(c2);

		Analysis an = new NonlinearStaticAnalysis(m, 10);
		an.getStrategy().addObserver(new StressObserver(m));
		// an.getStrategy().setEchoLevel(EchoLevelType.DEBUG);
		an.getStrategy().setEchoLevel(EchoLevelType.OUTPUT);
		// ((NonlinearStaticAnalysis) an).initializeTimeFactor(10);
		an.run();

		/*
		 * print the displacement and check with theoretical values
		 */
		System.out.println("testSolve1 results:");
		// double[][] Uh = new double[][] { { 0, 0 }, { 0.5, 0 }, { 0.5, -0.15
		// }, { 0, -0.15 } };
		for (int i = 0; i < 4; i++) {
			double[] uHatLocal = m.getMesh().getNode(i).getUHatLocal();
			System.out.println(ArrayFormat.format(uHatLocal));
			// Assert.assertEquals(Uh[i][0], uHatLocal[0], 1e-10);
			// Assert.assertEquals(Uh[i][1], uHatLocal[1], 1e-10);
		}
	}

	@Test
	public void testSolve2() throws SolveFailedException {

		Model m = createModel(createMeshQ4());

		Mesh mesh = m.getMesh();

		double du = 1;
		Constraint c1 = new Constraint(false, false);
		c1.setValue(0, du);
		Constraint c2 = new Constraint(false, true);
		c2.setValue(0, du);
		mesh.getNode(1).setConstraint(c1);
		mesh.getNode(2).setConstraint(c2);

		Analysis an = new LinearStaticAnalysis(m);
		an.getStrategy().addObserver(new StressObserver(m));
		// an.getStrategy().setEchoLevel(EchoLevel.DEBUG);
		an.getStrategy().setEchoLevel(EchoLevelType.OUTPUT);
		((NonlinearStaticAnalysis) an).initializeTimeFactor(10);
		an.run();

		/*
		 * print the displacement and check with theoretical values
		 */
		System.out.println("testSolve2 results:");
		// double[][] Uh = new double[][] { { 0, 0 }, { 0.5, 0 }, { 0.5, -0.15
		// }, { 0, -0.15 } };
		for (int i = 0; i < 4; i++) {
			double[] uHatLocal = m.getMesh().getNode(i).getUHatLocal();
			System.out.println(ArrayFormat.format(uHatLocal));
			// Assert.assertEquals(Uh[i][0], uHatLocal[0], 1e-10);
			// Assert.assertEquals(Uh[i][1], uHatLocal[1], 1e-10);
		}
	}

	public void execute() {
		Model m = createModel(createMeshT3());

		Mesh mesh = m.getMesh();

		double du = 1;
		Constraint c1 = new Constraint(false, false);
		c1.setValue(0, du);
		Constraint c2 = new Constraint(false, true);
		c2.setValue(0, du);
		mesh.getNode(1).setConstraint(c1);
		mesh.getNode(2).setConstraint(c2);

		Analysis an = new NonlinearStaticAnalysis(m, 1000);
		an.getStrategy().addObserver(new StressObserver(m));
		// an.getStrategy().setEchoLevel(EchoLevelType.DEBUG);
		an.getStrategy().setEchoLevel(EchoLevelType.OUTPUT);
		// ((NonlinearStaticAnalysis) an).initializeTimeFactor(10);
		ResistantGraphicalObserver ro = new ResistantGraphicalObserver(m, an.getStrategy(), 1,
				mesh.getNode(0), mesh.getNode(3));
		try {
			ro.setOutputToFile("/media/data1/java-learn-win/fem2012/src-misc/patch-test-curve.txt");
		} catch (IOException e) {
			e.printStackTrace();
			throw new Error("set output failed");
		}
		an.getStrategy().addObserver(ro);

		an.run();

		/*
		 * print the displacement and check with theoretical values
		 */
		System.out.println("testSolve1 results:");
		// double[][] Uh = new double[][] { { 0, 0 }, { 0.5, 0 }, { 0.5, -0.15
		// }, { 0, -0.15 } };
		for (int i = 0; i < 4; i++) {
			double[] uHatLocal = m.getMesh().getNode(i).getUHatLocal();
			System.out.println(ArrayFormat.format(uHatLocal));
			// Assert.assertEquals(Uh[i][0], uHatLocal[0], 1e-10);
			// Assert.assertEquals(Uh[i][1], uHatLocal[1], 1e-10);
		}
	}
}
